home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS02.ADF / Tutorials / menus < prev    next >
Text File  |  1989-05-30  |  25KB  |  544 lines

  1.  
  2.                       MENUS, REQUESTORS, AND GADGETS
  3.                                    BY
  4.                              John T. Draper
  5.                   A service from the Programmers Network
  6.                         on the WELL in Sausalito.
  7.  
  8.    Permission to post this on other networks is granted provided the
  9.  source of this information is included.   The programmers network is
  10.  a non-profit network exchange of programming information.   For more
  11.  information,  mail your requests to:
  12.  
  13.  WELL: crunch
  14.  BIX:  crunch
  15.  USENET: ihnp4!ptsfa!well!crunch
  16.  DELPHI: crunch                                 
  17.  
  18.  
  19.         ========================= MENUS =========================
  20.  
  21.     Menus are activated by pressing the RIGHT mouse button, causing 
  22.  little windows to open up along the top of the screen.   When the right
  23.  mouse button is pressed,  a row of words are displayed at the top.  Each
  24.  of these words are the name of the menu. The row of menu names along
  25.  the top are called a MENU STRIP.   
  26.  
  27.     When you point to a name, press the mouse button;  a little window
  28.  or rectangular square is displayed showing the names of the menu ITEMS.
  29.  Notice that, as a menu name is selected,  the name is inverted in a dark
  30.  rectangle.   The width of the rectangle ( in the menu strip),  is
  31.  the MENU NAME WIDTH.   Just below the menu strip is a box.   Contained
  32.  within the box are names for the menu ITEMS.   The width of the box I 
  33.  will call ITEM BOX WIDTH.   The menu name width and the item box width
  34.  have to be chosen properly in order for the menus to work and they are
  35.  related in a specific way.   If they are set up improperly,  they 
  36.  won't work.
  37.  
  38.     It is possible to write procedures that automatically set up the
  39.  proper widths,  but that is beyond the scope of this article.   Such
  40.  a procedure might take up an  unnecessary amount of memory.
  41.  
  42.     Amiga programs use two types of memory: that allocated by initialized
  43.  and declared variables and structures; and that allocated by the memory
  44.  manager supplied by intuition.   This program can easily be modified to
  45.  allocate menus outside of the program memory area, thus making more room
  46.  for large programs.
  47.  
  48.     This example attempts to use the "best of both worlds"  by using a
  49.  short piece of code to initialize and link the Menu items, and yet
  50.  having the flexability offered by initializing the structures at compile
  51.  time.
  52.  
  53.     There are advantages and dis-advantages to each method.   I will attempt
  54.  to explain them here.
  55.  
  56.  METHOD #1 - Initializing the structures at declaration.   For example:
  57.  
  58.  struct Menu req_menu = {
  59.     NL,                             /* NO Next menu           */
  60.     FIL_WIDTH + ITEM_WIDTH,         /* LeftEdge               */
  61.     0, REQ_WIDTH, 10,               /* TopEdge, Width, Height */
  62.     MENUENABLED,                    /* Flags                  */
  63.     "Requestors",                   /* Menu name              */
  64.     req_items                       /* Pointer to items list  */
  65.  };
  66.  
  67.     This code initializes and declares the structure at compile time.
  68.  This is nice for quick programs,  or programs that are small.   BUT!!-
  69.  here's the clincher!!   If your program has a lot if structures to
  70.  initialize,   IT INCREASES COMPILE TIMES. It also clutters up the program, 
  71.  because you have to read through pages and pages of these structure 
  72.  declarations to change your menus.   You cannot easily add or  remove 
  73.  items without going to several places in your code and changing it.   
  74.  It also takes up more program space.   Perhaps it doesn't matter on the 
  75.  Amiga,  but on the Macintosh,  there is a limited size your code module 
  76.  can have.   In the example program that comes with this article,  I use 
  77.  method #1 for ONLY the menus and NOT the menu items, because there is 
  78.  only a limited number of menus,  due to the size of the top strip.  
  79.  (menu bars for those Mac freaks).
  80.  
  81.  METHOD #2 - Calling a function to initialize an array of structures.
  82.  This is what I do in this example.   In this example,   I am only
  83.  using text items- but only a little bit of modification will allow
  84.  for IMAGE items.   The sprite editor,  soon to be here,  contains these
  85.  types of menus.   Usually,  only ONE intuitext and MenuItem structure
  86.  is initialized and are then copied into an array of them.   This array
  87.  can be allocated using the memory manager, then released when finished.
  88.  
  89.  
  90.  
  91.  DATA STRUCTURES USED IN MENUS
  92.  -----------------------------
  93.    
  94.    If you plan on having menus in your program,  you have to prepare two
  95.  basic types of structures,  and initialze their values.   These structures
  96.  have to be initialized properly in order for your program to work.  In
  97.  my first experience of using menus,  I spent over a week figuring them out.
  98.  The intuition manual wasn't too clear on how to set them up.   There
  99.  was an omission of an important field on page 6-15 of the intuition
  100.  manual.   The field is "SelectFill", which is between ItemFill and
  101.  Command.   You should mark this in your manual so you won't be confused.
  102.  
  103.  
  104.  MENU STRUCTURE                                 
  105.  --------------
  106.  
  107.     A "Menu" structure describes the characteristics of the "thingey" that's
  108.  at the TOP of the screen.   It describes LeftEdge,  TopEdge,  Width,
  109.  and Height.   This is NOT, I repeat NOT, the width and height of the
  110.  little window that pops out when that menu is selected.   For instance,
  111.  TopEdge and Height are IGNORED by Intuition.   Leftedge describes the
  112.  leftmost position RELATIVE TO THE SCREEN, where the black
  113.  select box will be rendered.   Eight pixels to the RIGHT of this box is
  114.  where the first character of the NAME of the menu will be rendered.
  115.  LeftEdge also cannot be a negative number.   The Leftedge of the NEXT
  116.  menu will equal the Leftedge + Width of the previous menu.
  117.  
  118.     Menu flags are provided to specify if the menu is currently enabled
  119.  or disabled (MENUENABLED),  or whether the Menus items are currently
  120.  displayed to the user.
  121.  
  122.     There is a pointer to the Menu name which will be displayed on the
  123.  top menu strip and a pointer to the FIRST MenuItem structure which
  124.  describes each ITEM the menu is to have.
  125.  
  126.                                                 
  127.  ITEMS STRUCTURE
  128.  ---------------
  129.  
  130.     Each menu usually has more than one ITEM.   An ITEM name is displayed
  131.  inside an ITEM BOX.   An ITEM BOX is the little box you see pop up if you
  132.  point and press the RIGHT mouse button at a specific menu name along the
  133.  top MENU STRIP.  MenuItem structures contain information that describes
  134.  the ITEM BOX and the item name.  
  135.  
  136.     Other information included in the MenuItem structure include a pointer
  137.  to the NEXT item in the menu.   Each item has its own set of flags that
  138.  can be used to tell Intuition to put a checkmark on the item.   You
  139.  can check the MenuItem whether it's checked by checking the CHECKED flag.
  140.  You also have to have the CHECKIT flag set.
  141.  
  142.     If your menu items are text names,  then you set the ITEMTEXT flag.
  143.  You can also specify an optional command key activation.   Setting
  144.  the COMMSEQ key, then putting the ascii value of the key into the
  145.  "Command" field, will cause that item to be selected when that key
  146.  and the RIGHT AMIGA key are pressed at the same time.
  147.  
  148.     If you want the item to be enabled and activated,  as most do,  then
  149.  set the ITEMENABLED flag.
  150.  
  151.     There are flags to describe how you want the highlighting to be done
  152.  when the item is selected.  HIGHCOMP will draw the selection box in the
  153.  Inverse mode,  while HIGHBOX will draw a box around the selection box.
  154.  I tried BOTH HIGHCOMP and HIGHBOX,  but nothing appeared to be selected.
  155.  
  156.     If you are using Images in your menu,  set the HIGHIMAGE flag.   You 
  157.  will usually want to set the HIGHBOX flag,  because it will stand out
  158.  better.   If you set the HIGHCOMP instead of the HIGHBOX,  the image
  159.  will be inverted;  it might not look right, especially if you are using
  160.  the Images to display color pattettes.
  161.  
  162.     An easy routine called NewMenu has been included which automatically
  163.  fills in the menuItem structure.   A description of it is listed below
  164.  in the "checklist" for setting up the menus.   This allows the use of
  165.  the Memory manager to allocate a lot of menu-item structures.
  166.  
  167.  
  168.  HANDLING MENU ACTIONS
  169.  ---------------------
  170.                                                 
  171.     Now that we have created the menus,  and have them popping out of the 
  172.  top of the screen,  we have to USE THEM.   Somehow,  we have to set up
  173.  our program so that certain actions can happen when the menu is activated
  174.  by the user.   This happens when the user lets go of the mouse button
  175.  after an item is selected.   Below is a "Checklist" which will enable you
  176.  to set up your program accurately.
  177.  
  178.  ____1.  In the NewWindow structure,  make sure the MENUPICK flag is specified.
  179.          If not,  then you will never get any Menu related events from Intuition.
  180.          The Intuition manual doesn't make this clear.
  181.  
  182.  ____2.  After setting the IDCMP flag MENUPICK in the NewWindow function,  you
  183.          have to make sure ALL the menu structures,  MenuItem structures,
  184.          and Intuitext or Image structures associated with MenuItem structures
  185.          are initialized properly.   After calling OpenWindow,  you must call
  186.          your Menu initialization procedure.   In this example,  I call it
  187.          NewMenu.   NewMenu essentually initializes the MenuItem,  structures
  188.          related to your menu.   It also fills in the pointer to the IntuiText
  189.          structure if the menu items are text,  or Image structure if the
  190.          menu contains ICONS or images.   You pass it the following:
  191.                                                 
  192.          o - Address of the Menu structure.  Ex:   &fmenu,  if the menu
  193.              structure is called fmenu.
  194.  
  195.          o - Address of an array of menu item name pointers.  These pointers
  196.              are put into the Intuitext items ONLY if the ITEMTEXT flag is set.
  197.              if not, then this pointer is a pointer to a bitmap image.
  198.  
  199.          o - Pointer to the first MenuItem structure in a linked list of other
  200.              structures.   This routine links them for you:  just tell
  201.              it where the FIRST MenuItem structure is,  and arrange for the
  202.              other structures to be right next to it.  Just declare an array
  203.              of MenuItem structures  (See the examples).   There are as many
  204.              MenuItem structures as there are Menu items.
  205.  
  206.          o - Pointer to the First intuitext structure in an array of them.  
  207.              There are as many IntuiText structures as there are MenuItem
  208.              structures.  If the ITEMTEXT flag is NOT set,  then we assume
  209.              that we are pointing to an Image structure.
  210.  
  211.          o - The Number of items this menu is to have.
  212.  
  213.          o - The Widths of each of the ITEM BOX WIDTHS.  Remember, the Item
  214.              box width is the pop up window width which contains the text item
  215.              names or the Images.
  216.  
  217.          o - MenuItem Flags.  All MenuItem structures will be filled with these
  218.              flags.   If you need different flags set,  you should do them
  219.              individually after calling this (these) functions.
  220.  
  221.  
  222.  ____3.  If any of the MenuItems need DIFFERENT flag settings.  Do them NOW.
  223.  
  224.  ____4.  Attach the menustrup to the window.   Call SetMenuStrip (See Example)
  225.          to tell Intuition to "Recognize" these new structures we just set up.
  226.  
  227.  ____5.  Now, modify the Event loop to "Recognize" menu actions by adding
  228.          a new "case" statement as shown below.   MENUPICK is the new case
  229.          we need to add.
  230.  
  231.     for (;;)
  232.     {
  233.   
  234.        if ((message = (struct IntuiMessage *)GetMsg(w->UserPort)) == 0L)  {
  235.            Wait(1L<<w->UserPort->mp_SigBit);    
  236.            continue;
  237.        }
  238.          class = message->Class;
  239.          code = message->Code;
  240.          ReplyMsg(message);
  241.          switch (class) {
  242.   
  243.             case CLOSEWINDOW : close_things();
  244.                                exit(0);
  245.                                break;
  246.   
  247.             case MENUPICK    : if (MENUNUM(code) != MENUNULL)
  248.                                  domenu(MENUNUM(code), ITEMNUM(code),
  249.                                         SUBNUM(code));
  250.                                break;
  251.   
  252.             case MOUSEBUTTONS: break;
  253.          }   /* Case */
  254.     }  /* for */
  255.  
  256.  ____6.  Now we define "domenu" as shown above.   We pass it three arguments,
  257.          and it is responsible for selecting the proper thing to do.   These
  258.          arguments are:
  259.      
  260.          o - Menu number....Specifies which menu.   Menu number 0 is the
  261.              left most menu on the screen.   The next one is menu number 1.
  262.  
  263.          o - Item number....Specifies the menu ITEM number from 0 to n.  
  264.  
  265.          o - Submenu item...Usually equals to 32 if there are none.
  266.  
  267.          See the example program included in this tutorial.   Normally,
  268.          your code might look like the example, but it usually depends
  269.          on your personal taste.  I used the "case" example because it
  270.          is easy to read.   A true C jock might hammer it into a more
  271.          efficient form.
  272.  
  273.  REQUESTERS - The easy kind
  274.  --------------------------
  275.  
  276.     Requesters turned out to be a real pain in the ....   The Intuition
  277.  manual really lacks good documentation,  and because of the kindness of
  278.  Jim, and Dave Lucas at Amiga R&D,  I was able to sucessfully create a
  279.  custom requester.                              
  280.  
  281.     Even simple AutoRequest turned out to be a pain because of the way
  282.  the arguments are passed to AutoRequest.   Because the Lattice C thinks
  283.  of "int's" as 32 bits,  and Manx C uses "int's" as 16 bits,   It wasn't
  284.  possible to pass the arguments properly without the "L" after the literal
  285.  digit.   After much fussing around with the borders,  and gadget rectangles
  286.  I was able to make a few sample AutoRequests.   The thing you have to
  287.  remember when using AutoRequests- If you're using a C compiler using
  288.  16 bit ints- is that the size and position arguments are 32 bits.
  289.  
  290.    val = AutoRequest(w, &AutoText, &TRUEtext, &FALSEtext, 0L, 0L, 319L, 60L );
  291.    if (val)
  292.         printf("TRUE\n");
  293.     else
  294.         printf("FALSE\n");
  295.     
  296.  
  297.     Above is a sample of how to pass numeric arguments to AutoRequest.
  298.  Note the "L" after the digits.  AutoText, TRUEtext, and FALSEtext are
  299.  IntuiText structures.   If you are using Lattice C,  remove the "L's"
  300.  shown above.
  301.                                                 
  302.     This function essentually "steals" control from your main "Message
  303.  loop",  and handles messages ONLY generated by the requester.   You
  304.  COULD use the main program event loop and add an extra case statement
  305.  switching on REQCLEAR messages.
  306.  
  307.  
  308.  
  309.  BUILDING YOUR OWN REQUESTERS
  310.  ----------------------------
  311.  
  312.     In order to build your own requester,   you will need to declare one or
  313.  more "Requester" structures:  at least ONE gadget AND border per structure.   
  314.  Naturally, you will probably want your own custom Image or Text describing 
  315.  to the user what the requester is to do.
  316.  
  317.  Example:
  318.  struct Requester req;     /* Custom Requester structure */
  319.  
  320.     After all the structures are declared,   you will have to call
  321.  InitRequester, passing it a pointer to your "Requester" structure.
  322.  NOTE:  In the Intuition manual,  the example showing the declaration
  323.  and initialization of the Requester structure really isn't necessary.
  324.  InitRequester will zap all of those fields anyway.   After talking to
  325.  Amiga folks,  they admitted that it really isn't appropriate to 
  326.  pre-initialize the Requester structure.
  327.  
  328.  Example:
  329.     InitRequester(&req);          /* Init the requestor              */
  330.  
  331.  
  332.     You DON'T HAVE TO Initialize the "Requester" structure,  because
  333.  Intuition does it FOR you.   InitRequester will ZERO OUT all the fields
  334.  in the Requester structure,  and also do other things.
  335.  
  336.     AFTER calling InitRequester,  you then have to initialize the specific
  337.  fields in the Requester structure as shown below.   This is usually done
  338.  AFTER creating the window, and BEFORE entering the event loop.
  339.  
  340.     req.LeftEdge  = 20;
  341.     req.TopEdge   = 20;
  342.     req.Width     = 250;
  343.     req.Height    = 80;
  344.     req.ReqGadget = &ongad;        /* First gadget in Requester */
  345.     req.ReqText   = &text;         /* Text for requester */
  346.     req.BackFill  = 1;             /* BackGnd color to window */
  347.     req.Flags     = 0;
  348.     req.ReqBorder = &out_border;  /* OutSide bord - Must have at least one */
  349.   
  350.     &ongad = Pointer to the first gadget in the requester.
  351.  
  352.     &text  = Pointer to Intuitext text structure and renders text into
  353.              the requester.
  354.  
  355.     &out_border = This is the outside border to the structure.   You MUST 
  356.              have at least ONE border structure.
  357.  
  358.  
  359.  USING CUSTOM REQUESTERS
  360.  -----------------------
  361.  
  362.     This was the part where I had the most problems.   I had to get help
  363.  from Amiga to figure this out.   
  364.  
  365.     Lets suppose the programmer wants to bring up a requester when a menu
  366.  choice is made.   According to the Intuition manual,   you call
  367.  Request()  function,  and the requester is supposed to come up on the
  368.  sceen.   It does.   Just fine.    HOWEVER-  what then??!?  
  369.  
  370.     You are then supposed to go into your own "wait" loop,  getting
  371.  messages from the IDCMP.   You are waiting for Intuition to send you
  372.  a REQCLEAR message class.   This means your NewWindow structure BETTER
  373.  have the REQCLEAR flag set,  otherwise you have no way to tell when
  374.  the user presses the gadget in your request.
  375.  
  376.     After you get the REQCLEAR message,  you will need to snatch a copy
  377.  if the IAddress field of the IntuiMessage structure BEFORE you reply
  378.  to the message.    The IAddress field contains the Address of the
  379.  gadget structure acted upon by the user.   You can use the GadgetID
  380.  field as your own personal method of identifying the gadget and act on it
  381.  accordingly.
  382.  
  383.     When you have finished processing this action,   you might think that
  384.  the appropriate thing to do would be to call EndGadget.   But as you
  385.  can see,  I DON'T.  I set the ENDGADGET flag in the Activation
  386.  field of the gadget.  If this flag is set,  you DON'T have to call
  387.  EndGadget; and if you do,  your system goes away.   Intuition closes
  388.  the Requester for you when THAT gadget is selected.    You can have
  389.  MORE THAN ONE gadget with the ENDGADGET set.   If you DON'T set the
  390.  ENDGADGET,  then it's safe to call "EndRequest".   A good way to tell
  391.  when Intuition trashes your requester is to examine the FirstRequest
  392.  field in the Window record.   The "Request" function stuffs the
  393.  FirstRequest field with a pointer to the Requester structure.   EndRequest
  394.  removes this pointer and sets it to NULL.    It might be safe to
  395.  use this procedure while calling EndRequest.
  396.  
  397.      if (w->FirstRequest != NULL)
  398.          EndRequest (&req, w);
  399.  
  400.  
  401.  PROCEDURE FOR MAKING AND USING CUSTOM REQUESTERS
  402.  ------------------------------------------------
  403.  
  404.  o - Sketch out the requester in rough draft form.   Decide how many
  405.      gadgets the requester is to have.
  406.  
  407.  o - Declare and initialize all the Gadget,  Intuitext,   image or
  408.      other structures needed by this requester.   Remember to set the
  409.      REQGADGET flag in the GadgetType fields of ALL the gadgets.   Also,
  410.      set the ENDGADGET flag in the gadget's Activation field if you want
  411.      the gadget to automatically go away after processing the REQCLEAR
  412.      message.   This can take several pages of structure definitions.
  413.      
  414.      If your Requester Gadget uses an Image instead of text,  you have
  415.      to set BOTH  GADGIMAGE | GADGHIMAGE flags in the "Flags" field
  416.      and then leave pointers to their "Image" structure like the
  417.      following example:
  418.  
  419.  o - If you want to process a gadget within a requester,  but leave the
  420.      requester up on the screen,  that gadget MUST have the 
  421.      GADGIMMEDIATE flag set as shown below.   This causes Intuition to
  422.      send you a GADGETDOWN message class.   Remember to SET the 
  423.      GADGETDOWN flag in the NewWindow structure,  otherwise you WON'T
  424.      get the Message back from Intuition.
  425.      
  426.      
  427.  struct Gadget face_gad = {
  428.    NULL,
  429.    30, 18,                              /* LeftEdge, TopEdge     */
  430.    32, 20,                              /* Width,  Height        */
  431.    GADGHIMAGE | GADGIMAGE | GADGHIMAGE, /* Flag                  */
  432.    RELVERIFY | GADGIMMEDIATE,           /* Activation            */
  433.    BOOLGADGET | REQGADGET,              /* GadgetType            */
  434.    (APTR)&smil_face,                    /* GadgetRender - Border */
  435.    (APTR)&tong_face,                    /* SelectRender          */
  436.    &gag_me,                             /* "Gag me"              */
  437.    NL, NL, NL, NL                       /* Mut Excl, Spec Info,  */
  438.  };
  439.  
  440.      The &smil_face is a pointer to an "Image" structure.  See listing
  441.      included in this tutorial.
  442.  
  443.  o - If you are building your own custom requesters,  you will need to
  444.      set the IDCMP flag REQCLEAR in your NewWindow structure as long as
  445.      you expect Intuition to clear your requester for you by setting the
  446.      ENDGADGET flag in the gadget's Activation field,  and expect to 
  447.      get the REQCLEAR message from IDCMP.   If you DON'T set this flag, 
  448.      then YOU must call "EndRequest" to remove your requester.
  449.  
  450.  o - In your initialization code somewhere before your message loop,   you
  451.      will need to call InitRequester.   Pass it the pointer to your
  452.      requester structure.   This clears out all the fields of your
  453.      requester and does other internal initializations.   Example:
  454.  
  455.     InitRequester(&req);          /* Init the requestor              */
  456.  
  457.      You need to do this for Every requester you expect to have.
  458.  
  459.  o - Initialize the fields in your requester array.   It should always
  460.      be done AFTER calling InitRequester because it zeroes out all the
  461.      elements in the structure.   Example:
  462.  
  463.  /* Init the fields in the Requester structure */
  464.   
  465.     req.LeftEdge  = 20;
  466.     req.TopEdge   = 20;
  467.     req.Width     = 250;
  468.     req.Height    = 80;
  469.     req.ReqGadget = &ongad;        /* First gadget */
  470.     req.ReqText   = &text;         /* Text for requester */
  471.     req.BackFill  = 1;             /* BackGnd color to window */
  472.     req.Flags     = 0;
  473.     req.ReqBorder = &out_border;  /* Must have at least one */
  474.  
  475.   
  476.  CALLING UP THE REQUESTER
  477.  ------------------------                       
  478.  
  479.  o - Call "Request", pass it a pointer to your request structure and a
  480.      pointer to your window.   This brings up the requester box in your
  481.      window.   The code listed below checks the returned value.  If
  482.      the value is 1,  then it was sucessful.  "do_req(&req)" does the next
  483.      thing explained below.
  484.      
  485.  
  486.              case   TEX_REQS: if ((val = Request(&req, w)) == 1) {
  487.                                 do_req(&req);
  488.                               }
  489.                               break;
  490.  
  491.  
  492.  o - Go into a Wait and GetMsg loop "listening" for the REQCLEAR message
  493.      class.   Before "Replying" to the message,  store the IAddress field
  494.      and the class of the IntuiMessage into local variables,  then Reply
  495.      to the Message.   If the "class" is equal to REQCLEAR,  then use the
  496.      IAddress value (which points to the gadget that user pressed) to
  497.      identify the gadget, and do the appropriate action.    You DON'T
  498.      have to call EndRequest after handling gadget action IF you
  499.      set the ENDGADGET flag in the gadget structure that was explained
  500.      above.  If you have to process any gadgets,  you have to check for
  501.      GADGETDOWN message class and process it.    The mes->IAddress
  502.      value below contains a pointer to the Gadget structure chosen
  503.      by the user.    I use the GadgID field in the gadget structure
  504.      to identify the gadget.    I don't necessarily need to know which
  505.      requester had the gadget.   I could find out by looking at the
  506.      FirstGadget field in the Window record,  then traversing through
  507.      the linked list to the LAST requester in the list.
  508.  
  509.  
  510.  do_req(request)
  511.  struct Requester *request;          /* Which requester to process */
  512.  {
  513.        int looping = TRUE;
  514.        struct IntuiMessage *mes;
  515.        struct Gadget *gad;           /* Gadget chosen */
  516.        ULONG class;
  517.   
  518.        while (looping)
  519.        {
  520.            if ((mes = (struct IntuiMessage *)GetMsg(w->UserPort)) == 0L) {
  521.               Wait(1L<<w->UserPort->mp_SigBit); 
  522.               continue;     /** Be nice to the other programs **/
  523.            }
  524.               class = mes->Class;
  525.               gad = (struct Gadget *)mes->IAddress;
  526.               ReplyMsg(mes);
  527.               if (class == REQCLEAR) {
  528.                  looping = FALSE;          /* We exit and Intuition will */
  529.               }                            /* remove the requester  */
  530.               if (class == GADGETDOWN) {
  531.   
  532.                  switch (gad->GadgetID) {
  533.   
  534.                     case TRUE_BUTT:  printf("true button ...\n");
  535.                                      break;
  536.                     case FALSE_BUTT: printf("false button...\n");
  537.                                      break;
  538.                     case FACE:       printf("Ouch!!..That hurt!!\n");
  539.                                      break;
  540.                  }
  541.               }
  542.        }
  543.  }                                              
  544.